
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:rebellion_udp/rebellion_udp_platform_interface.dart';

class RebellionUdpCommon extends RebellionUdpPlatform {
  RawDatagramSocket? socket;
  String? _multicastAddress;
  int? _port;

  static final RebellionUdpCommon _instance = RebellionUdpCommon._internal();

  factory RebellionUdpCommon() {
    return _instance;
  }

  RebellionUdpCommon._internal();

  @override
  void close() {
    if (socket != null) {
      socket!.close();
      socket = null;
    }
  }

  @override
  Future<void> init(String multicastAddress, int port) async {
    if(socket == null){
      _port = port;
      _multicastAddress = multicastAddress;

      socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port);
      socket!.multicastLoopback = false;
      socket!.multicastHops = 1;
      try {
        socket!.joinMulticast(InternetAddress(multicastAddress));
        if (kDebugMode) {
          print("Socket join multicast address $multicastAddress");
        }
      } on Exception catch(ex){
        if (kDebugMode) {
          print("join multicast $multicastAddress error. $ex");
        }
      }
    }
  }

  @override
  Future<bool?> isOpen() async {
    return socket != null;
  }

  @override
  void listen({required Function(Uint8List p1) read, required Function() write, required Function() closed}) {
    if(socket != null) {
      socket!.listen((event) {
        switch (event) {
          case RawSocketEvent.read:
            if (kDebugMode) {
              print("Socket read ready.");
            }
            Datagram? datagram = socket!.receive();
            if (datagram != null) {
              if (kDebugMode) {
                print("Datagram ${datagram.data}");
              }
              read(datagram.data);
            }
            break;
          case RawSocketEvent.write:
            if (kDebugMode) {
              print("Socket ready to write.");
            }
            write();
            break;
          case RawSocketEvent.closed:
            if (kDebugMode) {
              print("Socket close.");
            }
            closed();
            break;
          default:
            throw "Unexpected event $event";
        }
      });
    }
  }

  @override
  void send(Uint8List data) {
    if(socket != null) {
      socket!.send(
        data.toList(),
        InternetAddress(_multicastAddress!),
        _port!,
      );
    }
  }

  @override
  void sendToTarget(String ip, Uint8List data) {
    if(socket != null) {
      socket!.send(data.toList(), InternetAddress(ip), _port!);
    }
  }
}